<?php
/*
 * Copyright (c) 2008 Sergey Bondari, bondari@bondari.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace Mosaic\System;

/**
 * Request entity
 *
 * Base class for all requests.Generates unique request token amongst site to identify requests in queues .
 * Communicates with multiplexor.
 *
 * @package System
 * @subpackage Requests
 */
class Request implements ifRequestEntity {
	/**
	 * Unique request identification token
	 * @var string
	 */
	protected $__RID;

	/**
	 * Response container (?)
	 */
	protected $__RESPONSE;

	
	/**
	 * Type of request
	 * @see RequestType
	 * @var integer
	 */
	protected $__REQUEST_TYPE;

	/**
	 * Request execution type sync/async
	 * @var integer
	 */
	protected $__EXEC_TYPE;

	/**
	 * Context tag of request
	 * @var string
	 */
	protected $__CONTEXT;

	/**
	 * Origin
	 * @var string
	 */
	protected $__ORIGIN;

	/**
	 * Destination
	 * @var string
	 */
	protected $__DESTINATION;


	/**
	 * Class constructor. 
	 *
	 * Generates a unique identification token.
	 */
	function __construct() {
		global $sys;
		$this->__RID = md5($sys->name . uniqid(rand(), true));
		$this->__EXEC_TYPE = SYNC;
	}

	/**
	 * Serializes instance into the MCGI packet
	 *
	 * @return \Mosaic\MCGI\Packet
	 */
	public function createMCGIPacket() {
		global $sys;
		$mcgi = new \Mosaic\MCGI\Packet();
		$mcgi->setHeader('REQUEST_TOKEN', $this->getRequestToken());
		$mcgi->setHeader('REQUEST_TYPE', $this->getRequestType());
		$mcgi->setHeader('EXEC_TYPE', $this->getExecType());
		$mcgi->setHeader('CONTEXT', $this->getContextTag());
		if ($this->getDestination()) $mcgi->setHeader('DESTINATION' , $this->getDestination());
		$mcgi->setHeader('ORIGIN', $sys->name);
		return $mcgi;
	}

	/**
	 * Unserializes instance from MCGI packet
	 *
	 * @param \Mosaic\MCGI\Packet
	 * @return void
	 */
	public function fillFromMCGIPacket(\Mosaic\MCGI\Packet $mcgi) {
		$this->setRequestToken($mcgi->getHeader('REQUEST_TOKEN'));
		$this->setExecType($mcgi->getHeader('EXEC_TYPE'));
		$this->setContextTag($mcgi->getHeader('CONTEXT'));
		$this->__ORIGIN = $mcgi->getHeader('ORIGIN');
		if ($mcgi->getHeader('DESTINATION')) $this->__DESTINATION = $mcgi->getHeader('DESTINATION');
	}

	/**
	 * Request main unserialization routing. Calls 
	 *
	 * Creates proper Request instance from MCGIPacket instance. Fills instance with data using Request::fillFromMCGIPacket()
	 *
	 * @see Request::fillFromMCGIPacket()
	 * @param \Mosaic\MCGI\Packet 
	 * @return ifRequest Correspondent request class child.
	 */
	static function createFromMCGIPacket(\Mosaic\MCGI\Packet $mcgi) {
		switch ($mcgi->getHeader('REQUEST_TYPE')) {
			case RequestType::HTTP_CONTENT:
				$req = new HTTPContentRequest($mcgi->getHeaders());
			break;
			case RequestType::NODE_UP_MANIFEST:
				$req = new NodeUpManifest(NULL,NULL,NULL);
			break;
			case RequestType::NS_NODE_RESOLVE:
				$req = new NodeResolveRequest(NULL);
			break;
			case RequestType::NODE_DOWN_MANIFEST:
				$req = new NodeDownManifest(NULL);
			break;
			case RequestType::NS_REMOTE_CLASS_MANIFEST:
				$req = new RemoteClassManifest(NULL,NULL,NULL);
			break;
			case RequestType::NS_REMOTE_CLASS_RESOLVE:
				$req = new RemoteClassResolveRequest(NULL);
			break;
			case RequestType::REMOTE_OBJECT_INSTANTIATE:
				$req = new RemoteObjectInstantiateRequest(NULL,NULL);
			break;
			case RequestType::REMOTE_OBJECT_CALL:
				$req = new RemoteObjectCallRequest(NULL,NULL,NULL);
			break;
			case RequestType::REMOTE_OBJECT_STUB:
				$req = new RemoteObjectStubRequest(NULL);
			break;
			case RequestType::PING:
				$req = new PingRequest(NULL);
			break;
			case RequestType::RESOURCE:
				$req = new ResourceRequest(NULL);
			break;
			case RequestType::RELEASE_CONTEXT:
				$req = new ReleaseContextRequest();
			break;
			default:
				throw new RuntimeException('Cannot recognize request type \''.$mcgi->getHeader('REQUEST_TYPE').'\'');

		}

		$req->fillFromMCGIPacket($mcgi);
		return $req;
	}


	/**
	 * Request type getter
	 * @return integer Type of request
	 * @see class RequestType
	 */
	public function getRequestType() {
		return $this->__REQUEST_TYPE;
	}

	/**
	 * Request token getter
	 * @return string
	 */
	public function getRequestToken() {
		return $this->__RID;
	}


	/**
	 * Request token setter
	 * @param string
	 * @return void
	 */
	public function setRequestToken($rid) {
		$this->__RID = $rid;
	}

	/**
	 * Execution type setter
	 * @param SYNC|ASYNC
	 * @return void
	 */
	public function setExecType($type) {
		$this->__EXEC_TYPE = $type;
	}

	/**
	 * Exec type setter
	 */
	public function getExecType() {
		return $this->__EXEC_TYPE;
	}


	/**
	 * Response setter
	 */
	public function setResponse(Response $res) {
		$this->__RESPONSE = $val;
	}


	/**
	 * Response getter
	 */
	public function getResponse() {
		return $this->__RESPONSE;
	}

	/**
	 * Context tag setter 
	 * @param $v string Context tag
	 */
	public function setContextTag($v) {
		$this->__CONTEXT = $v;
	}

	/**
	 * Context tag getter
	 * @return string Context tag
	 */
	public function getContextTag() {
		return $this->__CONTEXT;
	}


	/**
	 * Sets destination node of the request
	 * @param string node name
	 */
	public function setDestination($nodeName) {
		$this->__DESTINATION = $nodeName;
	}

	/**
	 * Request destination getter
	 * @return string Destination node name
	 */
	public function getDestination() {
		return $this->__DESTINATION;
	}


	/**
	 * Request origin getter
	 * @return string Originating node name
	 */
	public function getOrigin() {
		return $this->__ORIGIN;
	}




	/**
	 * This function performs a send-receive operation on a socket via MCGI protocol.
	 *
	 * This function is to be used only for non-multiplexed, synchronous communication. In MOSAIC kernel this function is used for communication with naming agent.
	 *
	 * @param $socket
	 * @return \Mosaic\System\Response
	 */
	public function sendReceive($socket) {
		$this->send($socket);
		$mcgi = new \Mosaic\MCGI\Packet();
		trigger_error('Receiving response ...');
		$mcgi->receive($socket);
		trigger_error('Response received.');
		return Response::createFromMCGIPacket($mcgi);
	}


	/**
	 * This function performs a send operation on a socket via MCGI protocol
	 * 
	 * @param $socket
	 */
	private function send($socket) {
		trigger_error("Sending request ...");
		$mcgi = $this->createMCGIPacket();
		$mcgi->send($socket);
		trigger_error("Request sent.");
	}

	/**
	 * Application call to run a request synchroniusly.
	 *
	 * Function passes request to multiplexor in synchronous execution mode. Will return only when response becomes available.
	 *
	 * @see RequestMultiplexor::processOutgoingRequest()
	 * @param string Destination node name
	 * @return mixed Response to the request
	 */
	public function execSync($dst = NULL) {
		global $sys;
		$this->__EXEC_TYPE = SYNC;
		$this->__DESTINATION = $dst;
		return $sys->requestBroker->processOutgoingRequest($this);
	}

	/**
	 * Application call to run a request asynchroniusly.
	 *
	 * Function passes request to multiplexor in asynchronous execution mode. Will return imediately.
	 *
	 * @see RequestMultiplexor::processOutgoingRequest()
	 * @param mixed Callback
	 * @param string Destination node name
	 * @return void
	 */
	public function execAsync($callback = null, $dst = NULL) {
		global $sys;
		$this->__EXEC_TYPE = ASYNC;
		$this->__DESTINATION = $dst;
		$sys->requestBroker->processOutgoingRequest($this, $callback);
	}



	function __toString() {
		return $this->getResponse();
	}
}
